package com.example.ymm.modules1.service.dd.ms;

import com.aliyun.dingtalkoauth2_1_0.Client;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponse;
import com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenResponseBody;
import com.aliyun.tea.TeaException;
import com.example.ymm.util.EmailUtil;
import com.example.ymm.util.RedisKeyManager;
import com.example.ymm.util.RedisUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author zeymo
 */
@Slf4j
@Service
public class AccessTokenH5Service2 {


    @Autowired
    RedisUtil redisUtil;

    private Client auth2Client;

    @Value("${dd_h5.appKey}")
    private String appKey;

    @Value("${dd_h5.appSecret}")
    private String appSecret;

    private volatile AccessToken accessToken;

    @Getter
    @Setter
    static class AccessToken {
        private String accessToken;
        private Long expireTimestamp;
    }

    /**
     * init for first accessToken
     */
    @PostConstruct
    public void init() throws Exception {
        if (Objects.isNull(appKey)) {
            throw new RuntimeException("please set application.properties app.appKey=xxx");
        }

        if (Objects.isNull(appSecret)) {
            throw new RuntimeException("please set application.properties app.appSecret=xxx");
        }

        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
        config.protocol = "https";
        config.regionId = "central";
        auth2Client = new Client(config);

//        int maxTryTimes = 3;
//        while (maxTryTimes-- > 0) {
//            if (refreshAccessToken()) {
//                break;
//            }
//            if (true) {
//                break;
//            }
//
//            Thread.sleep(100);
//        }
//
//        if (maxTryTimes <= 0) {
//            throw new RuntimeException("fail to get accessToken from remote, try 3 times, please check your appKey" +
//                    " and appSecret");
//        }
    }

    /**
     * schedule for refresh token when expired
     */
//    @Scheduled(fixedRate = 60 * 1000)
//    public void checkAccessToken() {
//        if (Objects.isNull(accessToken)) {
//            return;
//        }
//
//        // check before expired in 10 minutes
//        long advanceCheckTime = 10 * 60L;
//        if (accessToken.expireTimestamp - System.currentTimeMillis() > advanceCheckTime * 1000L) {
//            return;
//        }
//
//        refreshAccessToken();
//    }

    private String refreshAccessToken() {
        GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
                .setAppKey(appKey)
                .setAppSecret(appSecret);
        try {
            GetAccessTokenResponse getAccessTokenResponse = auth2Client.getAccessToken(getAccessTokenRequest);
            if (Objects.isNull(getAccessTokenResponse) || Objects.isNull(getAccessTokenResponse.body)) {
                log.error("AccessTokenService_getTokenFromRemoteServer getAccessToken return error," +
                        " response={}", getAccessTokenResponse);
                EmailUtil.sendEmail("1055162826@qq.com","获取h5 token异常 response 为空:",getAccessTokenResponse.toString());

                return null;
            }

            GetAccessTokenResponseBody body = getAccessTokenResponse.body;
            if (Objects.isNull(body.accessToken) || Objects.isNull(body.expireIn)) {
                log.error("AccessTokenService_getTokenFromRemoteServer getAccessToken invalid token, token or expireIn" +
                        " maybe null, accessToken={}, expireIn={}", body.accessToken, body.expireIn);
                EmailUtil.sendEmail("1055162826@qq.com","获取h5 token异常 body 为空:",body.toString());

                return null;
            }

            AccessToken accessToken = new AccessToken();
            accessToken.setAccessToken(body.accessToken);
            accessToken.setExpireTimestamp(System.currentTimeMillis() + body.expireIn * 1000);
            this.accessToken = accessToken;
            log.info("refresh access token success, expireIn={}", body.expireIn);
            return body.accessToken;
        } catch (TeaException e) {
            log.error("AccessTokenService_getTokenFromRemoteServer getAccessToken throw " +
                    "TeaException, errCode={}, errorMessage={}", e.getCode(), e.getMessage(), e);
            EmailUtil.sendEmail("1055162826@qq.com","获取h5 token异常errCode:"+e.getCode(),e.getMessage());

            return null;
        } catch (Exception e) {
            log.error("AccessTokenService_getTokenFromRemoteServer getAccessToken throw Exception", e);
            EmailUtil.sendEmail("1055162826@qq.com","获取h5_异常token:",e.getMessage());

            return null;
        }
    }

    public synchronized String getAccessToken(){
        String getUrlToken=null;
        try{
            String tokenKey = RedisKeyManager.dd_h5_access_token;
            String lockKey = RedisKeyManager.dd_h5_access_lock;
            if(!redisUtil.hasKey(tokenKey)){
                boolean isOpen  = redisUtil.setIfAbsent(lockKey,"1",1000L, TimeUnit.SECONDS);
                if(isOpen){
                    getUrlToken=refreshAccessToken();
                    redisUtil.set(tokenKey,getUrlToken,7000L);
                    return getUrlToken;
                }else{
                    //  Thread.sleep(1100L);//休眠等待执行
                    if(!redisUtil.hasKey(tokenKey)){
                        isOpen  = redisUtil.setIfAbsent(lockKey,"1",2L, TimeUnit.SECONDS);
                        if(isOpen){
                            getUrlToken=refreshAccessToken();
                            redisUtil.set(tokenKey,getUrlToken,7000L);
                            return getUrlToken;
                        }else{
                            redisUtil.remove(tokenKey);
                            getUrlToken=refreshAccessToken();
                            redisUtil.set(tokenKey,getUrlToken,7000L);
                            return getUrlToken;
                        }
                    }else{
                        return redisUtil.getString(tokenKey);
                    }
                }
            }else{
                return  redisUtil.getString(tokenKey);
            }
        }catch (Exception e){
            return null;
        }
    }


//    public boolean isTokenNearlyExpired() {
//        // if expired timestamp nearly 5000ms, should not send requests
//        return accessToken.expireTimestamp < System.currentTimeMillis() - 5000L;
//    }
}
